import ProductCompareAPI from '@/api/ProductCompare';
import {
  Box,
  ClickModal,
  DragAndDropProvider,
  DraggableItem,
  DragHandle,
  DroppableContainer,
  FieldCols,
  Icon,
  mapField,
  PaginationTable,
  SearchFields,
  SelectWithEmpty,
  useCall,
  useFormValue,
  usePaginationCommon,
  useReq,
  useSet,
  useSimpleMemo,
} from '@/components';
import { enumToOptions, forkHandler, isEqualDeep, uid, wait } from '@/utils';
import { Button, Checkbox, Form, Input, message, Select, Spin } from 'antd';
import React, { useRef } from 'react';

const typeMap = { 1: '保障对比', 2: '投保规则', 3: '公司信息' };
const companies = {
  registeredCapital: '注册资本',
  comNature: '公司性质',
  peoControl: '实际控制人',
  customerTellphone: '公司电话',
  website: '公司地址',
  sarmra: 'SARMRA评分',
  cliaimTime: '理赔时效',
  cliaimMoney: '理赔总额',
  nameChn: '公司名称',
  createDate: '成立实际',
};

const insureds = {
  guaranteedValue: '保障期',
  paymentType: '缴费方式',
  paymentYear: '缴费期',
  pensionAge: '年金领取年龄',
  insuredAge: '年龄',
};

const ReadOnlyType = ({ value, ...rest }) => <Input value={typeMap[value]} readOnly {...rest} />;

function CategorySearch(props) {
  const { options, ...rest } = props;
  const nOptions = useSimpleMemo([
    //
    { value: null, label: '全部' },
    { value: '0', label: '无' },
    ...options,
  ]);
  return <Select options={nOptions} {...rest} />;
}

function CategoryEdit(props) {
  const { options, value, onChange } = props;
  const all = options?.map((v) => v.value);
  const toggle = useCall((e) => {
    const { checked } = e.target;
    onChange?.(checked ? all : []);
  });

  const boxChecked = value?.length > 0;
  const isIndeterminate = boxChecked && all.some((v) => !value?.includes(v));

  return (
    <Box flex dir='row' ali='center' shrink={false}>
      <Select {...props} />
      <Box px={0.5} />
      <Checkbox checked={boxChecked} indeterminate={isIndeterminate} onChange={toggle}>
        全选
      </Checkbox>
    </Box>
  );
}

const batchUpdate = (list, options) => {
  return Promise.all(
    list.map((item) =>
      ProductCompareAPI.insertOrUpdateDict(item, options).catch(() => Promise.resolve({ code: '000' })),
    ),
  ).then(() => Promise.resolve(null));
};

const EditDialog = (props) => {
  const { row, index, onOk, action = 'edit', ...rest } = props;
  const [form] = Form.useForm();
  const level = useFormValue(form, 'level');
  const type = useFormValue(form, 'type');
  const isCompanyType = level === '2' && type === '3';
  const isInsuredType = level === '2' && type === '2';
  const nameType = isInsuredType ? 'insured' : isCompanyType ? 'company' : '';

  const sortable = type === '1' || level !== '1';

  const onToggle = useCall((visible) => {
    if (visible) {
      form.resetFields();

      switch (action) {
        case 'add-child':
          form.setFieldsValue({
            parentCode: row.code,
            parent: row.name,
            code: row.type === '1' ? uid() : null,
            level: '2',
            type: row.type,
            sort: (row.compareCategoryList?.length ?? 0) + 1,
          });
          break;

        case 'add':
          form.setFieldsValue({ code: uid(), parentCode: null, level: '1', type: '1', sort: 1 });
          break;

        case 'edit':
          form.setFieldsValue(row);
          break;
      }
    }
  });

  const handleOk = useCall(async () => {
    const data = await form.validateFields();
    if (isCompanyType || isInsuredType) {
      data.name = companies[data.code];
    }
    console.log(data);
    return onOk?.(data);
  });

  if (action !== 'edit' && row && row.level !== '1') return null;

  const nameFields = {
    company: [
      [
        'code',
        '对比项名称',
        'select',
        {
          required: true,
          options: enumToOptions(companies),
        },
      ],
    ],
    insured: [
      [
        'code',
        '对比项名称',
        'select',
        {
          required: true,
          options: enumToOptions(insureds),
        },
      ],
    ],
  };

  const fields = [
    ['id', '', 'hidden', { noStyle: true }],

    ['level', '', 'hidden', { noStyle: true }],
    ['parentCode', '', 'hidden', { noStyle: true }],

    action === 'add-child' && ['parent', '父级对比项目', null, { readOnly: true }],
    ['type', '所属类型', ReadOnlyType],
    ...(nameFields[nameType] || [
      ['code', '', 'hidden', { noStyle: true }],
      ['name', '对比项名称', null, { required: true, maxLength: 10 }],
    ]),
    level === '2' && [
      'productCategory',
      '应用险类',
      'dict',
      {
        component: CategoryEdit,
        code: 'agency',
        hides: [''],
        mode: 'multiple',
        getValueFromEvent: (v) => v?.filter(Boolean).join(',') || null,
        getValueProps: (v) => ({ value: v?.split(',').filter(Boolean) }),
      },
    ],
    sortable
      ? ['sort', '排序', 'input-number', { min: 0, max: 99, precision: 0, step: 1 }]
      : ['_', '排序', null, { initialValue: '-', readOnly: true }],
  ]
    .filter(Boolean)
    .map(mapField);

  const content = (
    <Form component={false} form={form}>
      <FieldCols fields={fields} colProps={{ xs: 24 }} labelSpan={8} wrapperSpan={12} />
    </Form>
  );

  return <ClickModal title='新增对比项' {...rest} content={content} onToggle={onToggle} onOk={handleOk} />;
};

function DelButton({ row, onClick }) {
  const handleClick = useCall(() => {
    const hasChild = row.compareCategoryList?.length > 0;
    if (hasChild) return Promise.reject((message.warn('该对比项存在子类，不能直接删除'), 0));

    return onClick();
  });
  return <Button type='link' size='small' icon={<Icon type='delete' />} onClick={handleClick} />;
}

// { sorter: true, defaultSortOrder: 'descend',}
function CompareCategoryPage(props) {
  const { tableRef, searchData, onSearch, onSelect, selectedRef, reset } = usePaginationCommon();
  const xhr = useReq(batchUpdate);
  const columns = [
    {
      key: 'sorter',
      title: '排序',
      width: 64,
      render(_, row) {
        if (row.level === '1' && row.type !== '1') return null;
        return (
          <DragHandle>
            <Button size='small' type='text'>
              <Icon type='menu' />
            </Button>
          </DragHandle>
        );
      },
    },
    { dataIndex: 'name', title: '对比项名称', ellipsis: true },
    { dataIndex: 'productCategory', title: '应用险类', dict: 'agency', ellipsis: true },
    { dataIndex: 'type', title: '所属类型', map: typeMap, width: 100, ellipsis: true },
    // { dataIndex: 'sort', title: '排序', width: 72 },
  ];

  const fields = [
    [['t', 'name'], '对比项名称'],
    [
      ['t', 'productCategory'],
      '应用险类',
      'dict',
      { component: CategorySearch, code: 'agency', initialValue: null, hides: [''] },
    ],
    [['t', 'type'], '所属类型', SelectWithEmpty, { options: enumToOptions(typeMap) }],
  ].map(mapField);

  const insertOrUpdate = useCall(forkHandler((data) => ProductCompareAPI.insertOrUpdateDict(data), reset, true));
  const onDel = useCall(forkHandler((r) => ProductCompareAPI.deleteDict(r.id), reset, true));

  const actions = useRef({
    edit: {
      dialog: EditDialog,
      text(row) {
        return (
          (row.level !== '1' || row.type === '1') && (
            <Button type='link' size='small'>
              编辑
            </Button>
          )
        );
      },
      action: 'edit',
      onOk: (r, i, d) => insertOrUpdate(d),
    },
    addChild: {
      dialog: EditDialog,
      title: '新增子类',
      text(row) {
        return (
          (row.level !== '1' || row.type !== '2') && (
            <Button type='link' size='small'>
              新增子类
            </Button>
          )
        );
      },
      action: 'add-child',
      onOk: (r, i, d) => insertOrUpdate(d),
    },
    del: {
      confirm: void 0,
      onOk: onDel,
      text(row) {
        return <DelButton row={row} />;
      },
    },
  }).current;

  const [expandedKeys, expandedMisc] = useSet([], true);

  const childrenKey = 'compareCategoryList';

  const setDataSource = tableRef.current?.setData;

  const getDroppableKeys = useCall(async (allCtxMap, draggingCtx) => {
    const draggingRowKey = draggingCtx.row.id;
    const allPairs = Array.from(allCtxMap);
    const childrenKeys = [];
    const parentPair = allPairs.find(([_, { row }]) => {
      childrenKeys.push(...(row[childrenKey] ?? []).map((v) => v.id));
      return row[childrenKey]?.some((v) => v.id === draggingRowKey);
    });

    let droppableKeys = [];
    if (!parentPair) {
      droppableKeys = allPairs.reduce((ret, [key]) => ret.concat(childrenKeys.includes(key) ? [] : [key]), []);
    } else {
      droppableKeys = (parentPair[1].row[childrenKey] ?? []).map((v) => v.id);
    }
    const nextExpanded = expandedKeys.filter((v) => !droppableKeys.includes(v));
    if (!isEqualDeep(nextExpanded, expandedKeys)) {
      expandedMisc.set(nextExpanded);
    }

    await wait(0);

    return droppableKeys;
  });

  const onDrop = useCall((itemCtx, index, allCtxMap) => {
    const dataSource = tableRef.current.getData();

    const itemRowKey = itemCtx.row.id;
    const allPairs = Array.from(allCtxMap);
    const parentPair = allPairs.find(([_, { row }]) => {
      return row[childrenKey]?.some((v) => v.id === itemRowKey);
    });
    let updateList;

    const prevIndex = itemCtx.index;
    const currIndex = index;
    console.log({ prevIndex, currIndex });
    const start = Math.min(currIndex, prevIndex);
    const end = start + Math.abs(prevIndex - currIndex) + 1;

    if (!parentPair) {
      setDataSource((prev) => {
        const next = prev.slice();
        const [item] = next.splice(itemCtx.index, 1);
        next.splice(index, 0, item);
        updateList = next.slice(start, end);
        return next;
      });
    } else {
      const parentRow = parentPair[1].row;
      parentRow[childrenKey] = parentRow[childrenKey].slice();
      const [item] = parentRow[childrenKey].splice(itemCtx.index, 1);
      parentRow[childrenKey].splice(index, 0, item);

      updateList = parentRow[childrenKey].slice(start, end);
      setDataSource(dataSource.slice());
    }
    const min = Math.min(...updateList.map((v) => parseInt(v.sort)));
    updateList = updateList.map((item, i) => ({
      ...item,
      [childrenKey]: null,
      sort: min + i,
    }));
    xhr.start(updateList).then(reset);
  });

  return (
    <>
      <SearchFields fields={fields} onSubmit={onSearch} onReset={onSearch} />

      <Box flex dir='row' jus='between' py={2}>
        <div></div>

        <EditDialog action='add' onOk={insertOrUpdate}>
          <Button>+ 新增对比类别</Button>
        </EditDialog>
      </Box>

      <Spin spinning={xhr.status === 'loading'}>
        <DragAndDropProvider
          rowKey='id'
          itemComponent='tr'
          itemDraggingClassName='ant-table-row-selected'
          getDroppableKeys={getDroppableKeys}
          onDrop={onDrop}
        >
          <DroppableContainer>
            <PaginationTable
              ref={tableRef}
              serviceData={searchData}
              service={ProductCompareAPI.getDictList}
              columns={columns}
              actions={actions}
              actionCol={{ width: 200 }}
              expandable={useSimpleMemo({
                expandedRowKeys: expandedKeys,
                onExpandedRowsChange: expandedMisc.set,
                expandIconColumnIndex: 1,
                childrenColumnName: 'compareCategoryList',
              })}
              onRow={useRef((row, index) => ({ row, index })).current}
              resultToList={useCall((result) =>
                result?.data?.records.map(({ compareCategoryList: l, ...v }) => ({
                  ...v,
                  compareCategoryList: l?.length ? l : null,
                })),
              )}
              components={
                useRef({
                  table: 'table',
                  body: {
                    row: DraggableItem,
                    cell: 'td',
                  },
                }).current
              }
            />
          </DroppableContainer>
        </DragAndDropProvider>
      </Spin>
    </>
  );
}

export default CompareCategoryPage;
